#include <linux/rbtree.h>
#include <net/addrconf.h>
#include "socs/mod/pool6.h"
#include "socs/comm/types.h"
#include "socs/comm/constants.h"
#include "socs/comm/socs.h"
#include "socs/mod/addr_opt.h"
#include "socs/mod/neigh.h"
#include "socs/comm/config.h"

struct pool6_table *pool6tb;

static DEFINE_RWLOCK(pool6tb_lock);

static int __prefex6_compare_fn6(const struct pool6_entry *entry,
							 const struct ipv6_prefix *prefix6)
{
	return addr_cmp_prefix(IPV6, (void *)&entry->prefix6.address, (void *)&prefix6->address, 
		prefix6->len>entry->prefix6.len?entry->prefix6.len: prefix6->len);
}

static int __pool6_compare_fn6(const struct pool6_entry *entry,
							 const struct in6_addr *addr6)
{
	return addr_cmp_prefix(IPV6, (void *)&entry->prefix6.address, (void *)addr6,
						entry->prefix6.len);
}

static int __pool6_add(void *prefix6, struct pool6_table *pooltb)
{
	int ret = -1;
	struct pool6_entry *entry;

	if (!prefix6) {
		log_err(ERR_NULL_PTR, "%s NULL is not a valid prefix6.", __func__);
		return -1;
	}

	entry = kmalloc(sizeof(struct pool6_entry), GFP_ATOMIC);
	if (!entry) {
		log_err(ERR_NULL_PTR, "%s kmalloc rbtree entry failed.", __func__);
		return -1;
	}
	memset(entry, 0, sizeof(struct pool6_entry));
	memcpy(&entry->prefix6, prefix6, sizeof(struct ipv6_prefix));
	ret = rbtree_add(entry, &entry->prefix6.address,
				 &pool6tb->pool_tree, __pool6_compare_fn6,
				 struct pool6_entry,
				 tree_hook);

	if (!ret) {
		pool6tb->pool_num++;
	}

	return ret;
}

int __pool6_del_subprefix(struct ipv6_prefix *prefix, bool del_self)
{
	struct rb_node **node, *parent;
	struct pool6_entry *entry;
	rbtree_find_node(prefix, &pool6tb->pool_tree, __prefex6_compare_fn6, struct pool6_entry,
				tree_hook, parent, node);
	if (*node) {
		entry = rb_entry(*node, struct pool6_entry, tree_hook);
		if ((entry->prefix6.len > prefix->len) || 
			(del_self && entry->prefix6.len == prefix->len)) {
			// 树中的结点是要插入前缀的子网，删除它
			rb_erase(&entry->tree_hook, &pool6tb->pool_tree);
			kfree(entry);
			pool6tb->pool_num--;

			if ( pool6tb->pool_num > 0)
				__pool6_del_subprefix(prefix, del_self);
			return 1;
		}
		else
			return -EEXIST;
	}
	return 0;
}

// -----------------------------------------
int pool6_init(void)
{
	pool6tb = (struct pool6_table *)kmalloc(sizeof(struct pool6_table), GFP_ATOMIC);
	memset(pool6tb, 0, sizeof(struct pool6_table));
	pool6tb->pool_num = 0;
	pool6tb->pool_tree = RB_ROOT;

	return 0;
}

int is_prefix6(struct in6_addr *address)
{
	bool result = false;
	struct rb_node **node, *parent;
	struct pool6_entry *entry;

	if (ipv6_addr_type(address)&IPV6_ADDR_LINKLOCAL) {
		return result;
	}

	read_lock_bh(&pool6tb_lock);
	rbtree_find_node(address, &pool6tb->pool_tree, __pool6_compare_fn6, struct pool6_entry,
					 tree_hook, parent, node);
	if (*node) {
		entry = rb_entry(*node, struct pool6_entry, tree_hook);
		//printk("==is_prefix6 %pI6c, %pI6c/%d\n", address, &entry->prefix6.address, entry->prefix6.len);
		result = true;
	}
	read_unlock_bh(&pool6tb_lock);
	
	return result;
}

static void pool6_destructor(struct rb_node *node)
{
	struct pool6_entry *entry;
	entry = rb_entry(node, struct pool6_entry, tree_hook);
	kfree(entry);
}

void pool6_destroy(void)
{
	write_lock_bh(&pool6tb_lock);
	rbtree_clear(&pool6tb->pool_tree, pool6_destructor);
	kfree(pool6tb);
	write_unlock_bh(&pool6tb_lock);
}

int pool6_add(struct ipv6_prefix *prefix)
{
	int ret;

	write_lock_bh(&pool6tb_lock);
	
	// 查找最小长度前缀是否存在匹配
	ret = __pool6_del_subprefix(prefix, false);
	if (ret != -EEXIST)
		ret = __pool6_add(prefix, pool6tb);

	write_unlock_bh(&pool6tb_lock);

	return ret;
}

struct pool6_entry *pool6_iterator(struct pool6_table *pooltb, struct pool6_entry *entry)
{
	struct rb_node *node;

	if (pooltb) {
		node = rb_first(&pooltb->pool_tree);
	} else if (entry) {
		node = rb_next(&entry->tree_hook);
	}

	if (node) {
		return rb_entry(node, struct pool6_entry, tree_hook);
	}

	return NULL;
}

void pool6_print(void)
{
	struct pool6_entry *entry;

	read_lock_bh(&pool6tb_lock);

	entry = pool6_iterator(pool6tb, NULL);
	printk("--poolnum[%d]\n", pool6tb->pool_num);
	while (entry) {
		printk("%pI6c/%d\n", &entry->prefix6.address, entry->prefix6.len);
		entry = pool6_iterator(NULL, entry);
	}
	
	read_unlock_bh(&pool6tb_lock);
}

int pool6_remove(struct ipv6_prefix *prefix)
{
	int ret;

	write_lock_bh(&pool6tb_lock);
	
	ret = __pool6_del_subprefix(prefix, true);

	write_unlock_bh(&pool6tb_lock);

	return ret;
}

int pool6_flush(void)
{
	write_lock_bh(&pool6tb_lock);
	rbtree_clear(&pool6tb->pool_tree, pool6_destructor);
	pool6tb->pool_num = 0;
	write_unlock_bh(&pool6tb_lock);

	return 0;
}

int pool6_operat(struct arguments *args, char *buf)
{
	int  ret;
	switch(args->operation) {
	case ARGP_ADD:
		ret = pool6_add(&args->prefix6);
		if (ret == -EEXIST)
			sprintf(buf, "EXIST\n");
		else if (ret == 0)
			sprintf(buf, "SUCCESS\n");
		else
			sprintf(buf, "FAILURE\n");
		return ret;
	case ARGP_DISPLAY:
		pool6_print();
		sprintf(buf, "SUCCESS\n");
		return 0;
	case ARGP_REMOVE:
		ret = pool6_remove(&args->prefix6);
		sprintf(buf, "SUCCESS\n");
		return ret;
	case ARGP_FLUSH:
		ret = pool6_flush();
		sprintf(buf, "SUCCESS\n");
		return ret;
	default:
		return -1;
	}

	return 0;
}

